#include <iostream>
#include <cstring>
#include <thread>
#include <list>
#include <mutex>
extern "C"
{
    #include <sys/epoll.h>
    #include <unistd.h>
    #include <arpa/inet.h>
    #include <sys/socket.h>
    #include <pthread.h>
    #include <poll.h>
}



using namespace std;
mutex ljx;//随便定义的锁名

//每个房间储存对战用户、观战用户套接字的数据和epoll实例
struct  room{
    int connefd_P1=0;
    int connefd_P2=0;
    int connefd_O1=0;
    int connefd_O2=0;
    int connefd_O3=0;
    int connefd_O4=0;
    int connefd_O5=0;  
    int room_num=0;
    int epfd=0; 
    int ready1=0;
    int ready2=0;  
    int a=0,b=0,c=0,d=0,e=0,f=0,g=0;//用来标志客户端是加入或者退出
};
//全局初始一个大厅房间链表
list<room> Da_Ting; 


//测试代码
#if 1
ostream& operator<<(ostream& out, const room& p)
{
    out<<"epoll实例: "<<p.epfd<<"  房间号："<<p.room_num<<"  房主套接字："<<p.connefd_P1<<"  玩家2套接字:"<<p.connefd_P2<<"  观战1套接字:"<<p.connefd_O1<<"  观战2套接字："<<p.connefd_O2<<"  观战3套接字："<<p.connefd_O3<<"  观战4套接字："<<p.connefd_O4<<"  观战5套接字："<<p.connefd_O5<<endl;
    return out;
}

void showit(list<room> p){
    typename list<room>::iterator it = p.begin();
    for(; it != p.end(); ++it)
    {
        cout<<*it;
    }
}
#endif


class waitroom
{
private:
    char recv_buf[256];
    
    //未实现的房间功能：P1和P2都准备了的话，通知开启对战
public:                                                          
    //创建房间并开始监听所有成员   已内建房间新epoll   需要很多协议->（发送房主可已进入，监听逻辑，就未清楚每个room成员会接收到什么数据，又该做出什么反应)未实现
    void new_play_room()
    {
        int new_room_num=1;
        ljx.lock();

        for(int i=1;i<=100 ;++i)//查找房间链表里没有创建房间号                    
        {
            for(list<room>::iterator it = Da_Ting.begin() ; ; ++it )
            {
                if(it->room_num==i)
                {
                    new_room_num = 0;
                    break;
                }
                
                if(it == Da_Ting.end())
                break;

                new_room_num = i;
            }
            if(new_room_num!=0)
            break;
        }
        cout<<"创建"<<new_room_num<<"号房间"<<endl;
        room n_room;
        n_room.room_num = new_room_num;

        //epoll创建
        n_room.epfd = epoll_create(50);
        if(n_room.epfd < 0)
        {
            perror("epfd error");
        }

        Da_Ting.push_back(n_room);
        ljx.unlock();

        list<room>::iterator its = Da_Ting.begin();
        for( ; ; ++its )//查找新创建房间节点,若房间存在,则its指向该房间链表节点
        {
            
            if(its->room_num==n_room.room_num)
            {
                cout<<"找到"<<its->room_num<<"号房间"<<endl;
                
                break;
            } 
            
            if(its == Da_Ting.end())
            {
                cout<<"该房间不存在"<<endl;
                break;
            }
            
        }
        
        while(1)
        {
            //循环判断加入epoll
            if(its->connefd_P1!=0 && its->a==0)
            {
                //将进入房间的客户端通信套接字加入epoll监听
                cout<<"正在添加P1"<<endl;
                struct epoll_event event;
                event.events = EPOLLIN;
                event.data.fd = its->connefd_P1;
                int reval = epoll_ctl(its->epfd, EPOLL_CTL_ADD, its->connefd_P1, &event);
                if(reval < 0)
                {
                    perror("ctl_con error");
                }
                its->a=1;
            }
            if(its->connefd_P2!=0 && its->b==0)
            {
                //将进入房间的客户端通信套接字加入epoll监听
                cout<<"正在添加P2"<<endl;
                struct epoll_event event;
                event.events = EPOLLIN;
                event.data.fd = its->connefd_P2;
                int reval = epoll_ctl(its->epfd, EPOLL_CTL_ADD, its->connefd_P2, &event);
                if(reval < 0)
                {
                    perror("ctl_con error");
                }
                its->b=1;
            }
            if(its->connefd_O1!=0 && its->c==0)
            {
                //将进入房间的客户端通信套接字加入epoll监听
                cout<<"正在添加P"<<endl;
                struct epoll_event event;
                event.events = EPOLLIN;
                event.data.fd = its->connefd_O1;
                int reval = epoll_ctl(its->epfd, EPOLL_CTL_ADD, its->connefd_O1, &event);
                if(reval < 0)
                {
                    perror("ctl_con error");
                }
                its->c=1;
            }
            if(its->connefd_O2!=0 && its->d==0)
            {
                //将进入房间的客户端通信套接字加入epoll监听
                cout<<"正在添加P"<<endl;
                struct epoll_event event;
                event.events = EPOLLIN;
                event.data.fd = its->connefd_O2;
                int reval = epoll_ctl(its->epfd, EPOLL_CTL_ADD, its->connefd_O2, &event);
                if(reval < 0)
                {
                    perror("ctl_con error");
                }
                its->d=1;
            }
            if(its->connefd_O3!=0 && its->e==0)
            {
                //将进入房间的客户端通信套接字加入epoll监听
                cout<<"正在添加P"<<endl;
                struct epoll_event event;
                event.events = EPOLLIN;
                event.data.fd = its->connefd_O3;
                int reval = epoll_ctl(its->epfd, EPOLL_CTL_ADD, its->connefd_O3, &event);
                if(reval < 0)
                {
                    perror("ctl_con error");
                }
                its->e=1;
            }
            if(its->connefd_O4!=0 && its->f==0)
            {
                //将进入房间的客户端通信套接字加入epoll监听
                cout<<"正在添加P"<<endl;
                struct epoll_event event;
                event.events = EPOLLIN;
                event.data.fd = its->connefd_O4;
                int reval = epoll_ctl(its->epfd, EPOLL_CTL_ADD, its->connefd_O4, &event);
                if(reval < 0)
                {
                    perror("ctl_con error");
                }
                its->f=1;
            }
            if(its->connefd_O5!=0 && its->g==0)
            {
                //将进入房间的客户端通信套接字加入epoll监听
                cout<<"正在添加P"<<endl;
                struct epoll_event event;
                event.events = EPOLLIN;
                event.data.fd = its->connefd_O5;
                int reval = epoll_ctl(its->epfd, EPOLL_CTL_ADD, its->connefd_O5, &event);
                if(reval < 0)
                {
                    perror("ctl_con error");
                }
                its->g=1;
            }

            
             //循环判断删去epoll节点
            if(its->connefd_P1==0 && its->a==1)
            {
                //将进入房间的客户端通信套接字加入epoll监听
                cout<<"正在添加P1"<<endl;
                struct epoll_event event;
                event.events = EPOLLIN;
                event.data.fd = its->connefd_P1;
                int reval = epoll_ctl(its->epfd, EPOLL_CTL_DEL, its->connefd_P1, &event);
                if(reval < 0)
                {
                    perror("ctl_con error");
                }
                its->a=0;
            }
            if(its->connefd_P2==0 && its->b==1)
            {
                //将进入房间的客户端通信套接字加入epoll监听
                cout<<"正在添加P2"<<endl;
                struct epoll_event event;
                event.events = EPOLLIN;
                event.data.fd = its->connefd_P2;
                int reval = epoll_ctl(its->epfd, EPOLL_CTL_DEL, its->connefd_P2, &event);
                if(reval < 0)
                {
                    perror("ctl_con error");
                }
                its->b=0;
            }
            if(its->connefd_O1==0 && its->c==1)
            {
                //将进入房间的客户端通信套接字加入epoll监听
                cout<<"正在添加P"<<endl;
                struct epoll_event event;
                event.events = EPOLLIN;
                event.data.fd = its->connefd_O1;
                int reval = epoll_ctl(its->epfd, EPOLL_CTL_DEL, its->connefd_O1, &event);
                if(reval < 0)
                {
                    perror("ctl_con error");
                }
                its->c=0;
            }
            if(its->connefd_O2==0 && its->d==1)
            {
                //将进入房间的客户端通信套接字加入epoll监听
                cout<<"正在添加P"<<endl;
                struct epoll_event event;
                event.events = EPOLLIN;
                event.data.fd = its->connefd_O2;
                int reval = epoll_ctl(its->epfd, EPOLL_CTL_DEL, its->connefd_O2, &event);
                if(reval < 0)
                {
                    perror("ctl_con error");
                }
                its->d=0;
            }
            if(its->connefd_O3==0 && its->e==1)
            {
                //将进入房间的客户端通信套接字加入epoll监听
                cout<<"正在添加P"<<endl;
                struct epoll_event event;
                event.events = EPOLLIN;
                event.data.fd = its->connefd_O3;
                int reval = epoll_ctl(its->epfd, EPOLL_CTL_DEL, its->connefd_O3, &event);
                if(reval < 0)
                {
                    perror("ctl_con error");
                }
                its->e=0;
            }
            if(its->connefd_O4==0 && its->f==1)
            {
                //将进入房间的客户端通信套接字加入epoll监听
                cout<<"正在添加P"<<endl;
                struct epoll_event event;
                event.events = EPOLLIN;
                event.data.fd = its->connefd_O4;
                int reval = epoll_ctl(its->epfd, EPOLL_CTL_DEL, its->connefd_O4, &event);
                if(reval < 0)
                {
                    perror("ctl_con error");
                }
                its->f=0;
            }
            if(its->connefd_O5==0 && its->g==1)
            {
                //将进入房间的客户端通信套接字加入epoll监听
                cout<<"正在删P"<<endl;
                struct epoll_event event;
                event.events = EPOLLIN;
                event.data.fd = its->connefd_O5;
                int reval = epoll_ctl(its->epfd, EPOLL_CTL_DEL, its->connefd_O5, &event);
                if(reval < 0)
                {
                    perror("ctl_con error");
                }
                its->g=0;
            }


            //等待数据发生
            struct epoll_event reevent_bf[50];
            int size = epoll_wait(n_room.epfd, reevent_bf, 50, 0);
            if(size < 0)
            {
                perror("wait error");
                continue;
            }
            for(int num = 0; num < size; num++)
            {
                int reval;
                //客户端来数据
                if(reevent_bf[num].data.fd == its->connefd_P1 && reevent_bf[num].data.fd !=0)//P1房主发来讯息的响应
                {
                    reval = recv(reevent_bf[num].data.fd, recv_buf, sizeof(recv_buf), 0);
                    if(reval < 0)
                    {
                        perror("recv error");
                        close(reevent_bf[num].data.fd);
                    }
                    else if(reval == 0)
                    {
                        cout << "客户端下线\n";
                        close(reevent_bf[num].data.fd);
                    }
                    else
                    {
                        cout<<recv_buf;
                        // if(recv_buf[0]=='1')
                        // {
                        //     cout<<"shoudao:"<<recv_buf[0]<<endl;
                        // }
                        // if(recv_buf[0]=='0')
                        // {
                        //     cout<<"shoudao:"<<recv_buf[0]<<endl;
                        // }
                        // if(recv_buf[0]=='2')
                        // {
                        //     cout<<"shoudao:"<<recv_buf[0]<<endl;
                        // }
                        bzero(recv_buf, sizeof(recv_buf));

                    }
                }

                else if(reevent_bf[num].data.fd == n_room.connefd_P2&& reevent_bf[num].data.fd !=0)//P2玩家发来讯息的响应
                {
                    reval = recv(reevent_bf[num].data.fd, recv_buf, sizeof(recv_buf), 0);
                    if(reval < 0)
                    {
                        perror("recv error");
                        close(reevent_bf[num].data.fd);
                    }
                    else if(reval == 0)
                    {
                        cout << "客户端下线\n";
                        close(reevent_bf[num].data.fd);
                    }
                    else
                    {
                        cout<<recv_buf;
                        // if(recv_buf[0]=='1')
                        // {
                        //     cout<<"shoudao:"<<recv_buf[0]<<endl;
                        // }
                        // if(recv_buf[0]=='0')
                        // {
                        //     cout<<"shoudao:"<<recv_buf[0]<<endl;
                        // }
                        // if(recv_buf[0]=='2')
                        // {
                        //     cout<<"shoudao:"<<recv_buf[0]<<endl;
                        // }
                        bzero(recv_buf, sizeof(recv_buf));

                    }
                }

                else if(reevent_bf[num].data.fd == n_room.connefd_O1&& reevent_bf[num].data.fd !=0)//O1观战人发来退出观战讯息的响应
                {
                    reval = recv(reevent_bf[num].data.fd, recv_buf, sizeof(recv_buf), 0);
                    if(reval < 0)
                    {
                        perror("recv error");
                        close(reevent_bf[num].data.fd);
                    }
                    else if(reval == 0)
                    {
                        cout << "客户端下线\n";
                        close(reevent_bf[num].data.fd);
                    }
                    else
                    {

                        if(recv_buf[0]=='1')
                        {
 
                        }

                        bzero(recv_buf, sizeof(recv_buf));

                    }
                }

                else if(reevent_bf[num].data.fd == n_room.connefd_O2&& reevent_bf[num].data.fd !=0)//O2观战人发来退出观战讯息的响应
                {
                    reval = recv(reevent_bf[num].data.fd, recv_buf, sizeof(recv_buf), 0);
                    if(reval < 0)
                    {
                        perror("recv error");
                        close(reevent_bf[num].data.fd);
                    }
                    else if(reval == 0)
                    {
                        cout << "客户端下线\n";
                        close(reevent_bf[num].data.fd);
                    }
                    else
                    {

                        if(recv_buf[0]=='1')
                        {
 
                        }
            
                        bzero(recv_buf, sizeof(recv_buf));

                    }
                }

                else if(reevent_bf[num].data.fd == n_room.connefd_O3&& reevent_bf[num].data.fd !=0)//O3观战人发来退出观战讯息的响应
                {
                    reval = recv(reevent_bf[num].data.fd, recv_buf, sizeof(recv_buf), 0);
                    if(reval < 0)
                    {
                        perror("recv error");
                        close(reevent_bf[num].data.fd);
                    }
                    else if(reval == 0)
                    {
                        cout << "客户端下线\n";
                        close(reevent_bf[num].data.fd);
                    }
                    else
                    {

                        if(recv_buf[0]=='1')
                        {
 
                        }
            
                        bzero(recv_buf, sizeof(recv_buf));

                    }
                }

                else if(reevent_bf[num].data.fd == n_room.connefd_O4&& reevent_bf[num].data.fd !=0)//O2观战人发来退出观战讯息的响应
                {
                    reval = recv(reevent_bf[num].data.fd, recv_buf, sizeof(recv_buf), 0);
                    if(reval < 0)
                    {
                        perror("recv error");
                        close(reevent_bf[num].data.fd);
                    }
                    else if(reval == 0)
                    {
                        cout << "客户端下线\n";
                        close(reevent_bf[num].data.fd);
                    }
                    else
                    {

                        if(recv_buf[0]=='1')
                        {
 
                        }
            
                        bzero(recv_buf, sizeof(recv_buf));

                    }
                }

                else if(reevent_bf[num].data.fd == n_room.connefd_O5&& reevent_bf[num].data.fd !=0)//O2观战人发来退出观战讯息的响应
                {
                    reval = recv(reevent_bf[num].data.fd, recv_buf, sizeof(recv_buf), 0);
                    if(reval < 0)
                    {
                        perror("recv error");
                        close(reevent_bf[num].data.fd);
                    }
                    else if(reval == 0)
                    {
                        cout << "客户端下线\n";
                        close(reevent_bf[num].data.fd);
                    }
                    else
                    {

                        if(recv_buf[0]=='1')
                        {
 
                        }
            
                        bzero(recv_buf, sizeof(recv_buf));

                    }
                }

            }

        }





    }

    //玩家1进入房间
    static void P1_add_room(int connefd)
    {
        
        ljx.lock();
        list<room>::iterator it = Da_Ting.begin();
        for(int i=1;i<=5 ;++i)//查找房间链表里没P1的房间                    
        {
            for( ; ; ++it )
            {
                if(it->connefd_P1==0)
                {
                    break;
                }
                
                if(it == Da_Ting.end())
                {
                    cout<<"没有空房间"<<endl;
                    return;
                }
                
            }
            
            
        }
        it->connefd_P1=connefd;
 
        //将新房间的数据放房间链表里-----添加链表节点
        
        ljx.unlock();

    }

    //玩家2进入房间,进入被监听状态，如果房主走了新来的自动成为房主P1  ---需主线程服务器调用      需要通知 玩家 进入的协议->（未通知房主加人）
    static void P2_add_room(int connefd,int join_room_num)
    {
        ljx.lock();
        list<room>::iterator its = Da_Ting.begin();
        for( ; ; ++its )//查找要加入的房间节点,若房间存在,则its指向该房间链表节点
        {
            
            if(its->room_num==join_room_num)
            {
                cout<<"找到"<<its->room_num<<"号房间"<<endl;
                break;
            } 
            
            if(its == Da_Ting.end())
            {
                cout<<"该房间不存在"<<endl;
                break;
            }
            
        }
        if(its->connefd_P2==0)
        {
            its->connefd_P2=connefd;
            cout<<"添加到P2中"<<endl;
            //将进入房间的Play2客户端通信套接字加入epoll监听
            
        }
        else if(its->connefd_P1==0)
        {
            its->connefd_P1=connefd;
            cout<<"添加到P1中"<<endl;
            //将进入房间的Play客户端通信套接字加入epoll监听
        }
        else
        {
            cout<<"该房间正在进行对局"<<endl;
        }
        ljx.unlock();

    }

    //观战人进入对局房间,进入被监听状态 ---需主线程服务器调用    需要通知客户端 观战人 进入的协议->（未通知房主加人）
    static void observer_add_room(int connefd,int join_room_num)
    {
        ljx.lock();
        list<room>::iterator its = Da_Ting.begin();
        for( ; ; ++its )//查找要加入的房间节点,若房间存在,则its指向该房间链表节点
        {
            
            if(its->room_num==join_room_num)
            {
                cout<<"找到"<<its->room_num<<"号房间"<<endl;
                break;
            } 
            
            if(its == Da_Ting.end())
            {
                cout<<"该房间不存在"<<endl;
                break;
            }
            
        }
        cout<<"添加观战中"<<endl;

        //查找要加入的房间里还有没有观战位置，并将观战人通信套接字添加到该房间的epoll实例上监听
        if(its->connefd_O1==0)
        {
            its->connefd_O1=connefd;
            cout<<"添加O1中"<<endl;
        }
        else if(its->connefd_O2==0)
        {
            its->connefd_O2=connefd;
            //将进入房间的观战客户端通信套接字加入epoll监听
            cout<<"添加O2中"<<endl;
        }
        else if(its->connefd_O3==0)
        {
            its->connefd_O3=connefd;
            cout<<"添加O3中"<<endl;
        }
        else if(its->connefd_O4==0)
        {
            its->connefd_O4=connefd;
            cout<<"添加O4中"<<endl;
        }
        else if(its->connefd_O5==0)
        {
            its->connefd_O5=connefd;
            cout<<"添加O5中"<<endl;
        }
        else
        {
            cout<<"该房间观战位已满"<<endl;
        }
        cout<<"添加完"<<endl;
        ljx.unlock();
    }

    //退出房间 ---用于epoll识别到才进行的操作    （想办法通知主线程服务器添加）未实现
    static void exit(int connefd )
    {
        ljx.lock();
        //删除观战人在房间的epoll树节点
        list<room>::iterator its = Da_Ting.begin();
        for( ; ; ++its )//查找要删除的房间节点,若房间存在,则its指向该房间链表节点   并清0观战人所在的room链表节点里的数据
        {
            if(its->connefd_P1==connefd)
            {
                its->connefd_P1 = 0;
                break;
            }
            else if(its->connefd_P2==connefd)
            {
                its->connefd_P2 = 0;
                break;
            }
            else if(its->connefd_O1==connefd)
            {
                its->connefd_O1 = 0;
                break;
            }
            else if(its->connefd_O2==connefd)
            {
                its->connefd_O2 = 0;
                break;
            }
            else if(its->connefd_O3==connefd)
            {
                its->connefd_O3 = 0;
                break;
            }
            else if(its->connefd_O4==connefd)
            {
                its->connefd_O4 = 0;
                break;
            }
            else if(its->connefd_O5==connefd)
            {
                its->connefd_O5 = 0;
                break;
            } 
              
        }
        
        ljx.unlock();
   
    }

    //玩家准备函数  ---用于epoll识别到才进行的操作  需要客户端发来  准备  的协议--->（未写,每次调用判断一下ready1和ready2是否全置1）
    static void ready(int connefd)
    {

    }

    //观战者申请当玩家函数 ---用于epoll识别到才进行的操作  （未写）
    static void O_to_P()
    {

    }
    
};



void new_wait_room() //创建线程开新房间类
{
    waitroom R1;
    R1.new_play_room();
}

void da_ting_init()
{
    
}


int main()
{       

    thread t1(new_wait_room);   
    // thread t2(new_wait_room);
    // thread t3(new_wait_room);

 // 创建套接字
    int tcp_socket = socket(AF_INET, SOCK_STREAM, 0);
    if ( tcp_socket == -1 )
    {
        perror("socket ");
        return -1 ;
    }
    perror("socket ");
  
    // 设置服务器的IP地址信息
    struct sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
        //端口号
    serverAddr.sin_port = htons(9999);
        //具体的IP地址
    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);

    socklen_t addrlen = sizeof(serverAddr);

    // 把套接字与地址信息进行绑定
    if( bind(tcp_socket , (const struct sockaddr *) &serverAddr, addrlen))
    {
        perror("bind ");
        return -1; 
    }
    perror("bind ");


    // 设置监听
    if(listen(tcp_socket ,  0 ))
    {
        perror("listen ");
        return -1 ;
    }
    perror("listen ");


    // [阻塞] 等待连接请求 
    struct sockaddr_in clienAddr ;
    int connectfd = accept(tcp_socket , (struct sockaddr *) &clienAddr, &addrlen );
    if (connectfd ==-1 )
    {
        perror("accept");
        return -1 ;
    }

        
        

        // waitroom::observer_add_room(123,2);
        // waitroom::observer_add_room(122,3);
        waitroom::P2_add_room(12,1);
        waitroom::P1_add_room(connectfd);
        // waitroom::exit(123);
        // waitroom::exit(87);
        
        showit(Da_Ting);


        t1.join();
        // t2.join();
        // t3.join();

       
        

    

        //showit(Da_Ting);

}   